﻿using System;
using System.Diagnostics;
using System.Reflection;
using Faml.Frontend;

namespace Faml.Optimization
{
    public class MergeVisitor : IVisitor
    {
        private RuntimeEnvironment re;
        private MergeTable table;

        public MergeVisitor(RuntimeEnvironment re)
        {
            this.re = re;
            this.table = re.MergeTable;
        }

        #region IVisitor Members

        public FamlExpression Visit(FamlExpression node)
        {
            if (node.NodeType != FamlExpressionType.FunctionCall)
                return node;

            FamlFunctionCallExpression mce = (FamlFunctionCallExpression)node;
            MethodInfo opA = mce.Operation.Method;

            for (int i = 0; i < mce.Arguments.Count; i++)
            {
                if (mce.Arguments[i] is FamlFunctionCallExpression)
                {
                    FamlFunctionCallExpression mceB = (FamlFunctionCallExpression)mce.Arguments[i];
                    MethodInfo opB = mceB.Operation.Method;

                    try
                    {
                        MethodInfo opC = table.GetOptimizationForPair(opA, opB);
                        if (opC != null)
                        {
                            FamlExpression[] args = new FamlExpression[mce.Arguments.Count - 1 + mceB.Arguments.Count];
                            int j = 0;
                            for (; j < i; j++)
                            {
                                args[j] = mce.Arguments[j];
                            }
                            for (int k = 0; k < mceB.Arguments.Count; j++, k++)
                            {
                                args[j] = mceB.Arguments[k];
                            }
                            for (int k = i + 1; j < args.Length; j++, k++)
                            {
                                args[j] = mce.Arguments[k];
                            }

                            Debug.WriteLine("Optimization found for " + opA.Name + " & " + opB.Name + ".");

                            Operation operationC;
                            if (opC.IsStatic)
                            {
                                operationC = new Operation(opC.Name, opC);
                            }
                            else
                            {
                                object instance = re.OperationInstanceProvider(opC);
                                operationC = new Operation(opC.Name, opC, instance);
                            }


                            FamlExpression mceC = new FamlFunctionCallExpression(operationC, args);
                            return mceC;
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                    }
                }
            }

            return node;
        }

        #endregion
    }
}
